#include "global.h"
#include "staticfunctions.h"
#include "dcolordialog.h"
#include "trisets.h"
#include "geoshaderfactory.h"
#include "propertyeditor.h"

#include <QDomDocument>


Trisets::Trisets()
{
  m_trisets.clear();
  m_geoHighQualityShader = 0;
  m_geoShadowShader = 0;
}

Trisets::~Trisets()
{
  clear();
}

void
Trisets::clear()
{
  for (int i=0; i<m_trisets.count(); i++)
    delete m_trisets[i];

  m_trisets.clear();
}

void
Trisets::allGridSize(int &nx, int &ny, int &nz)
{
  nx = 0;
  ny = 0;
  nz = 0;

  if (m_trisets.count() == 0)
    return;

  m_trisets[0]->gridSize(nx, ny, nz);

  for(int i=1; i<m_trisets.count();i++)
    {
      int mx, my, mz;
      m_trisets[i]->gridSize(mx, my, mz);
      nx = qMax(nx, mx);
      ny = qMax(ny, my);
      nz = qMax(nz, mz);
    }
}

void
Trisets::allEnclosingBox(Vec& boxMin,
			 Vec& boxMax)
{
  if (m_trisets.count() == 0)
    return;

  boxMin = Vec(0,0,0);
  boxMax = Vec(0,0,0);

  m_trisets[0]->enclosingBox(boxMin, boxMax);

  for(int i=1; i<m_trisets.count();i++)
    {
      Vec bmin, bmax;
      m_trisets[i]->enclosingBox(bmin, bmax);
      boxMin = StaticFunctions::minVec(boxMin, bmin);
      boxMax = StaticFunctions::maxVec(boxMax, bmax);
    }
}

bool
Trisets::isInMouseGrabberPool(int i)
{
  if (i < m_trisets.count())
    return m_trisets[i]->isInMouseGrabberPool();
  else
    return false;
}
void
Trisets::addInMouseGrabberPool(int i)
{
  if (i < m_trisets.count())
    m_trisets[i]->addInMouseGrabberPool();
}
void
Trisets::addInMouseGrabberPool()
{
  for(int i=0; i<m_trisets.count(); i++)
    m_trisets[i]->addInMouseGrabberPool();
}
void
Trisets::removeFromMouseGrabberPool(int i)
{
  if (i < m_trisets.count())
    m_trisets[i]->removeFromMouseGrabberPool();
}

void
Trisets::removeFromMouseGrabberPool()
{
  for(int i=0; i<m_trisets.count(); i++)
    m_trisets[i]->removeFromMouseGrabberPool();
}


bool
Trisets::grabsMouse()
{
  for(int i=0; i<m_trisets.count(); i++)
    {
      if (m_trisets[i]->grabsMouse())
	return true;
    }
  return false;
}

void
Trisets::addTriset(QString flnm)
{
  TrisetGrabber *tg = new TrisetGrabber();
  if (tg->load(flnm))
    m_trisets.append(tg);
  else
    delete tg;
}

void
Trisets::postdraw(QGLViewer *viewer)
{
  for(int i=0; i<m_trisets.count();i++)
    {
      int x,y;
      m_trisets[i]->mousePosition(x,y);
      m_trisets[i]->postdraw(viewer,
			     x, y,
			     m_trisets[i]->grabsMouse(),
			     i);
    }
}

void
Trisets::predraw(QGLViewer *viewer,
		 double *Xform,
		 Vec pn,
		 bool shadows, int shadowWidth, int shadowHeight)
{
  for(int i=0; i<m_trisets.count();i++)
    m_trisets[i]->predraw(viewer,
			  Xform,
			  pn,
			  shadows, shadowWidth, shadowHeight);
}

void
Trisets::draw(QGLViewer *viewer,
	      Vec lightVec,
	      float pnear, float pfar, Vec step,
	      bool applyShadows, bool applyShadowShader, Vec eyepos)
{
  for(int i=0; i<m_trisets.count();i++)
    {
      if (! m_trisets[i]->pointMode())
	{
	  if (applyShadows &&
	      m_trisets[i]->shadows())
	    {
	      if (applyShadowShader)
		glUseProgramObjectARB(m_geoShadowShader);
	      else
		{
		  glUseProgramObjectARB(m_geoHighQualityShader);
		  glUniform3fARB(m_highqualityParm[0], eyepos.x, eyepos.y, eyepos.z);
		  glUniform1iARB(m_highqualityParm[1], 2); // blurred shadowBuffer
		}
	    }
	}

      m_trisets[i]->draw(viewer,
			 m_trisets[i]->grabsMouse(),
			 lightVec,
			 pnear, pfar, step);

      glUseProgramObjectARB(0);
    }

}

bool
Trisets::keyPressEvent(QKeyEvent *event)
{
  for(int i=0; i<m_trisets.count(); i++)
    {
      if (m_trisets[i]->grabsMouse())
	{
//	  if (event->key() == Qt::Key_P)
//	    {
//	      bool pm = m_trisets[i]->pointMode();
//	      m_trisets[i]->setPointMode(!pm);
//	      return true;
//	    }
	  if (event->key() == Qt::Key_Delete)
	    {
	      m_trisets[i]->removeFromMouseGrabberPool();
	      m_trisets.removeAt(i);
	      return true;
	    }
	  if (event->key() == Qt::Key_Space)
	    {
	      PropertyEditor propertyEditor;
	      QMap<QString, QVariantList> plist;

	      QVariantList vlist;

	      vlist.clear();
	      vlist << QVariant("double");
	      vlist << QVariant(m_trisets[i]->opacity());
	      vlist << QVariant(0.0);
	      vlist << QVariant(1.0);
	      vlist << QVariant(0.1); // singlestep
	      vlist << QVariant(1); // decimals
	      plist["opacity"] = vlist;

	      vlist.clear();
	      vlist << QVariant("double");
	      vlist << QVariant(m_trisets[i]->ambient());
	      vlist << QVariant(0.0);
	      vlist << QVariant(1.0);
	      vlist << QVariant(0.1); // singlestep
	      vlist << QVariant(1); // decimals
	      plist["ambient"] = vlist;

	      vlist.clear();
	      vlist << QVariant("double");
	      vlist << QVariant(m_trisets[i]->diffuse());
	      vlist << QVariant(0.0);
	      vlist << QVariant(1.0);
	      vlist << QVariant(0.1); // singlestep
	      vlist << QVariant(1); // decimals
	      plist["diffuse"] = vlist;

	      vlist.clear();
	      vlist << QVariant("double");
	      vlist << QVariant(m_trisets[i]->specular());
	      vlist << QVariant(0.0);
	      vlist << QVariant(1.0);
	      vlist << QVariant(0.1); // singlestep
	      vlist << QVariant(1); // decimals
	      plist["specular"] = vlist;

	      vlist.clear();
	      vlist << QVariant("color");
	      Vec pcolor = m_trisets[i]->color();
	      QColor dcolor = QColor::fromRgbF(pcolor.x,
					       pcolor.y,
					       pcolor.z);
	      vlist << dcolor;
	      plist["color"] = vlist;


	      vlist.clear();
	      vlist << QVariant("checkbox");
	      vlist << QVariant(m_trisets[i]->pointMode());
	      plist["pointmode"] = vlist;

	      vlist.clear();
	      vlist << QVariant("int");
	      vlist << QVariant(m_trisets[i]->pointsize());
	      vlist << QVariant(1);
	      vlist << QVariant(50);
	      plist["pointsize"] = vlist;

	      vlist.clear();
	      vlist << QVariant("int");
	      vlist << QVariant(m_trisets[i]->pointstep());
	      vlist << QVariant(1);
	      vlist << QVariant(50);
	      plist["pointstep"] = vlist;

	      vlist.clear();
	      vlist << QVariant("checkbox");
	      vlist << QVariant(m_trisets[i]->shadows());
	      plist["shadows"] = vlist;


	      QStringList keys;
	      keys << "color";
	      keys << "opacity";
	      keys << "gap";
	      keys << "ambient";
	      keys << "diffuse";
	      keys << "specular";
	      keys << "gap";
	      keys << "pointmode";
	      keys << "pointstep";
	      keys << "pointsize";
	      keys << "gap";
	      keys << "shadows";
	      

	      propertyEditor.set("Triset Parameters", plist, keys);
	      QMap<QString, QPair<QVariant, bool> > vmap;

	      if (propertyEditor.exec() == QDialog::Accepted)
		vmap = propertyEditor.get();
	      else
		return true;


	      keys = vmap.keys();

	      for(int ik=0; ik<keys.count(); ik++)
		{
		  QPair<QVariant, bool> pair = vmap.value(keys[ik]);

		  if (pair.second)
		    {
		      if (keys[ik] == "color")
			{
			  QColor color = pair.first.value<QColor>();
			  float r = color.redF();
			  float g = color.greenF();
			  float b = color.blueF();
			  pcolor = Vec(r,g,b);
			  m_trisets[i]->setColor(pcolor);
			}
		      else if (keys[ik] == "opacity")
			m_trisets[i]->setOpacity(pair.first.toDouble());
		      else if (keys[ik] == "ambient")
			m_trisets[i]->setAmbient(pair.first.toDouble());
		      else if (keys[ik] == "diffuse")
			m_trisets[i]->setDiffuse(pair.first.toDouble());
		      else if (keys[ik] == "specular")
			m_trisets[i]->setSpecular(pair.first.toDouble());
		      else if (keys[ik] == "pointmode")
			m_trisets[i]->setPointMode(pair.first.toBool());
		      else if (keys[ik] == "pointsize")
			m_trisets[i]->setPointSize(pair.first.toInt());
		      else if (keys[ik] == "pointstep")
			m_trisets[i]->setPointStep(pair.first.toInt());
		      else if (keys[ik] == "shadows")
			m_trisets[i]->setShadows(pair.first.toBool());
		    }
		}

//
//	      QString str;
//	      QString cmd;
//	      bool ok;
//	     
//	      str = "opacity, color, pointsize, pointstep\n";
//	      str += "ambient, diffuse, specular\n\n";
//	      str += QString("opacity : %1\n").arg(m_trisets[i]->opacity());
//	      str += QString("pointsize : %1\n").arg(m_trisets[i]->pointsize());
//	      str += QString("pointstep : %1\n").arg(m_trisets[i]->pointstep());
//	      str += QString("ambient : %1\n").arg(m_trisets[i]->ambient());
//	      str += QString("diffuse : %1\n").arg(m_trisets[i]->diffuse());
//	      str += QString("specular : %1\n").arg(m_trisets[i]->specular());
//	      str += QString("\n\n");
//	      str += QString("Vertices : (%1)    Triangles : (%2)\n").\
//		arg(m_trisets[i]->vertexCount()).arg(m_trisets[i]->triangleCount());
//	      str += QString("\nEnter command string");
//	      cmd = QInputDialog::getText(0,
//					  "Triset Commands",
//					  str,
//					  QLineEdit::Normal,
//					  "",
//					  &ok);
//	      if (ok && !cmd.isEmpty())
//		processCommand(i, cmd);	
	      
	      return true;
	    }
	}
    }

  return false;
}

void
Trisets::processCommand(int idx, QString cmd)
{
  bool ok;
  cmd = cmd.toLower();
  QStringList list = cmd.split(" ", QString::SkipEmptyParts);
  
  if (list[0] == "setopacity" || list[0] == "opacity")
    {
      if (list.size() == 2)
	{
	  float op = list[1].toFloat(&ok);
	  op = qBound(0.0f, op, 1.0f);
	  m_trisets[idx]->setOpacity(op);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }
  else if (list[0] == "setcolor" || list[0] == "color")
    {
      Vec pcolor = m_trisets[idx]->color();
      QColor dcolor = QColor::fromRgbF(pcolor.x,
				       pcolor.y,
				       pcolor.z);
      QColor color = DColorDialog::getColor(dcolor);
      if (color.isValid())
	{
	  float r = color.redF();
	  float g = color.greenF();
	  float b = color.blueF();
	  pcolor = Vec(r,g,b);
	  m_trisets[idx]->setColor(pcolor);
	}
    }
  else if (list[0] == "setambient" || list[0] == "ambient")
    {
      if (list.size() == 2)
	{
	  float amb = list[1].toFloat(&ok);
	  amb = qBound(0.0f, amb, 1.0f);
	  m_trisets[idx]->setAmbient(amb);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }
  else if (list[0] == "setdiffuse" || list[0] == "diffuse")
    {
      if (list.size() == 2)
	{
	  float diff = list[1].toFloat(&ok);
	  diff = qBound(0.0f, diff, 1.0f);
	  m_trisets[idx]->setDiffuse(diff);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }
  else if (list[0] == "setspecular" || list[0] == "specular")
    {
      if (list.size() == 2)
	{
	  float shine = list[1].toFloat(&ok);
	  shine = qBound(0.0f, shine, 1.0f);
	  m_trisets[idx]->setSpecular(shine);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }
  else if (list[0] == "setpointsize" || list[0] == "pointsize")
    {
      if (list.size() == 2)
	{
	  int ps = list[1].toInt(&ok);
	  ps = qBound(0, ps, 128);
	  m_trisets[idx]->setPointSize(ps);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }
  else if (list[0] == "setpointstep" || list[0] == "pointstep")
    {
      if (list.size() == 2)
	{
	  int ps = list[1].toInt(&ok);
	  ps = qBound(0, ps, 1000);
	  m_trisets[idx]->setPointStep(ps);
	}
      else
	QMessageBox::critical(0, "Triset Command Error",
				 "value not specified");
    }


}

void
Trisets::createHighQualityShader(bool shadows,
				 float shadowIntensity)
{
  if (m_geoHighQualityShader)
    glDeleteObjectARB(m_geoHighQualityShader);

  QString shaderString;

  shaderString = GeoShaderFactory::genHighQualityShaderString(shadows,
							      shadowIntensity);
  m_geoHighQualityShader = glCreateProgramObjectARB();
  if (! GeoShaderFactory::loadShader(m_geoHighQualityShader,
				     shaderString))
    exit(0);
  m_highqualityParm[0] = glGetUniformLocationARB(m_geoHighQualityShader, "eyepos");
  m_highqualityParm[1] = glGetUniformLocationARB(m_geoHighQualityShader, "shadowTex");
}

void
Trisets::createShadowShader(Vec attenuation)
{
  if (m_geoShadowShader)
    glDeleteObjectARB(m_geoShadowShader);

  float r = attenuation.x;
  float g = attenuation.y;
  float b = attenuation.z;

  QString shaderString;

  shaderString = GeoShaderFactory::genShadowShaderString(r,g,b);
  m_geoShadowShader = glCreateProgramObjectARB();
  if (! GeoShaderFactory::loadShader(m_geoShadowShader,
				     shaderString))
    exit(0);
}

void
Trisets::save(const char *flnm)
{
  QDomDocument doc;
  QFile f(flnm);
  if (f.open(QIODevice::ReadOnly))
    {
      doc.setContent(&f);
      f.close();
    }

  QDomElement topElement = doc.documentElement();

  for(int i=0; i<m_trisets.count(); i++)
    {
      QDomElement de = m_trisets[i]->domElement(doc);
      topElement.appendChild(de);
    }

  QFile fout(flnm);
  if (fout.open(QIODevice::WriteOnly))
    {
      QTextStream out(&fout);
      doc.save(out, 2);
      fout.close();
    }
}

void
Trisets::load(const char *flnm)
{
  QDomDocument document;
  QFile f(flnm);
  if (f.open(QIODevice::ReadOnly))
    {
      document.setContent(&f);
      f.close();
    }

  QDomElement main = document.documentElement();
  QDomNodeList dlist = main.childNodes();
  for(int i=0; i<dlist.count(); i++)
    {
      if (dlist.at(i).nodeName() == "triset")
	{
	  QDomElement de = dlist.at(i).toElement();

	  TrisetGrabber *tg = new TrisetGrabber();
	  if (tg->fromDomElement(de))	    
	    m_trisets.append(tg);
	  else
	    delete tg;
	}
    }

}

QList<TrisetInformation>
Trisets::get()
{
  QList<TrisetInformation> tinfo;

  for(int i=0; i<m_trisets.count(); i++)
    tinfo.append(m_trisets[i]->get());

  return tinfo;
}

void
Trisets::set(QList<TrisetInformation> tinfo)
{
  if (tinfo.count() == 0)
    {
      clear();
      return;
    }
    
  if (m_trisets.count() == 0)
    {
      for(int i=0; i<tinfo.count(); i++)
	{
	  TrisetGrabber *tgi = new TrisetGrabber();	
	  if (tgi->set(tinfo[i]))
	    m_trisets.append(tgi);
	  else
	    delete tgi;
	}

      return;
    }


  QVector<int> present;
  present.resize(tinfo.count());
  for(int i=0; i<tinfo.count(); i++)
    {
      present[i] = -1;
      for(int j=0; j<m_trisets.count(); j++)
	{
	  if (tinfo[i].filename == m_trisets[j]->filename())
	    {
	      present[i] = j;
	      break;
	    }
	}
    }

  QList<TrisetGrabber*> tg;
  tg = m_trisets;
  
  m_trisets.clear();
  for(int i=0; i<tinfo.count(); i++)
    {
      TrisetGrabber *tgi;

      if (present[i] >= 0)
	tgi = tg[present[i]];
      else
	tgi = new TrisetGrabber();
	
      if (tgi->set(tinfo[i]))
	m_trisets.append(tgi);
      else
	delete tgi;
    }

  for(int i=0; i<tg.count(); i++)
    {
      if (! m_trisets.contains(tg[i]))
	delete tg[i];
    }

  tg.clear();
}
